-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API for runtime parameters, e.g. for configuring federation upstream components. #150
API for runtime parameters, e.g. for configuring federation upstream components. #150
Conversation
- to support responses from GET requests.
-- note that doc.go is out of sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. This looks like a good addition but this specific revision introduces incorrect terminology that I cannot accept.
Specifically it confuses runtime parameters — one of the ways to configure stuff on a RabbitMQ node — with federation upstreams.
Federation upstreams are defined using runtime parameters. A federation upstream is a component (a type of runtime parameter) but there are other parameters (policies and dynamic Shovels are two examples), and more
are coming in future RabbitMQ versions.
In fact, you can see that the endpoint used starts with {prefix}/parameters
.
It's perfectly reasonable to have functions that specifically operate on federation upstreams
but we should keep the distinction clear. The good news is that with a few changes this PR would cover more ground than you've originally planned to.
I will leave a few specific comments in the diff.
federation.go
Outdated
// | ||
|
||
// ListFederationUpstreams returns all federation upstreams | ||
func (c *Client) ListFederationUpstreams() (rec []FederationUpstream, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be very nice to accompany this (and, perhaps, base it on) with a set of functions that would list, set, and delete arbitrary runtime parameters. Note that RabbitMQ nodes will perform parameter validation so if a component is not known (not registered by a plugin or RabbitMQ core), it will be rejected.
We can also add functions that work with parameters of a certain type. Then federation upstream management would rely on them and pass federation-upstream
for component. How does this sound?
Note that the suggested API changes would not require a lot of new tests. An absolute minimum would do, and the existing federation upstream tests would provide most coverage. |
Sounds good. When I first came to adding support for managing federation upstreams to terraform, I expected to be defining parameter types and resources. However, I was swayed by the existence of both Federation and Shovel types already present in rabbithole. I probably should have opened a discussion beforehand. I think adding a set of methods that operate explicitly on runtime parameters aligns well with the underlying RabbitMQ APIs. The docs describe using vhost-scoped parameters to configure settings at run time. I use your suggested name of A possible API design for managing runtime parameters might look like this. This mirrors the underlying HTTP API (except for the order of path params) and follows existing rabbithole API conventions. The corresponding HTTP API calls are shown in the comments. This is very much influenced by the Federation and Shovel plugins, but perhaps there are other things to consider? There are already methods for managing Shovels. type RuntimeParameter struct {
Name string `json:"name"`
Vhost string `json:"vhost"`
Component string `json:"component"`
Value RuntimeParameterValue `json:"value"`
}
// could also just use the go type directly or interface{}
type RuntimeParameterValue map[string]interface{}
// /api/parameters A list of all vhost-scoped parameters.
ListRuntimeParameters()
=> []RuntimeParameter, error
// /api/parameters/component A list of all vhost-scoped parameters for a given component.
ListRuntimeParametersFor(component string)
=> []RuntimeParameter, error
ListRuntimeParametersIn(vhost, component string)
=> []RuntimeParameter, error
// /api/parameters/component/vhost/name An individual vhost-scoped parameter.
GetRuntimeParameter(vhost, component, name string)
=> *RuntimeParameter, error
PutRuntimeParameter(vhost, component, name string, value RuntimeParameterValue)
=> error
DeleteRuntimeParameter(vhost, component, name string)
=> error Thoughts? |
The above API were shown as public methods. But separate API for managing runtime parameters and federation upstream might be redundant or confusing. I think this is what you were getting at. I got it. For example. // federation.go
func (c *Client) GetFederationUpstream(vhost, upstreamName string) (param *RuntimeParameter, err error) {
return getRuntimeParameter(vhost, "federation-upstream", ¶m)
}
// runtime_parameter.go
func getRuntimeParameter(vhost, name string, rec interface{}) err error {
// ...
executeAndParseRequest(c, req, &rec)
// ...
} Let me put something together for review. |
The proposed API makes sense to me. Additional public functions that operate specifically on upstreams also make sense to me. Your example with |
Thank you for your willingness to invest more time into this PR! |
-- just one large test for the time being
Okay, please review these changes. This is still WIP, but I wanted to check in before moving completing the changes. Changes so far
This allows you to work with parameter values containing arbitrary data. See this test.
I have only added a couple of temporary V2 methods to show one approach to using the runtime parameter API to manage different types of runtime parameters.
This approach requires a Other approaches worth considering.
This is a common approach, but requires extra code to safely access map values. However, it would remove the need for the This sort of thing. // inside GetFederationUpstream
param, err := GetRuntimeParameter()
info := &FederationInfo{
Name: param.Name,
Definition: FederationDefinition{}
}
m := param.Value.(map[string]interface{})
if v, ok := m["uri"].(string); ok {
info.Definition.Uri = v
}
return info
// etc
type FederationInfo struct {
RuntimeParameter
Value FederationDefinition `json:"value"`
}
// decodes to the following json
// {
// "name": "temporary",
// "vhost": "rabbit/hole",
// "component": "federation-upstream",
// "value": {
// "uri": "amqp://127.0.0.1/%2f",
// "expires": 0,
// "message-ttl": 0,
// "max-hops": 0,
// "prefetch-count": 1000,
// "reconnect-delay": 1,
// "ack-mode": "on-confirm",
// "trust-user-id": false,
// "exchange": "",
// "queue": ""
// }
// } There is probably a more elegant solution to all of this, but this seems pretty consistent with the overall API now. Thoughts? |
This looks close to what I had in mind. Constructing a federation upstream (info if you will) struct from a runtime parameter struct makes most sense to me. Embedding the parameter feels like a loss of "encapsulation" for no particular benefit. I personally think that Thanks 🙏 |
- this removes the need for PopulateRuntimeParameter
- use consistent variable names
Thank you so much! This is a great addition to the library. |
What this PR does:
Why do we need this PR?
Notes
Some notes from working on this PR.
API changes
To support the new read methods, I added additional fields to
FederationUpstream
and introduced aFederationDefinitionDTO
type. This is an exact copy of the existing shovel implementation, which makes sense, because they are more or less the same thing.Default values
The only required field for a federation upstream definition is
Uri
. Although many of the other arguments have stated defaults in the docs, when a value is not provided, it gets the default value for the go type instead.For example:
Is the value for
prefetch-count
the default of1000
, or0
? I don't think these values are the same, although I haven't tested this to be honest. It is easy to supply the default values in a request, which is probably the right thing to do. However, some arguments, such asexpires
, use a default value of none (according to the docs) or leave blank (in the Admin UI) to mean never expires. Does0
accomplish the same thing?Might it be a good idea to annotate these fields with
omitempty
instead?Deleting a federation upstream parameter
Deleting a non-existent upstream returns a
HTTP 404
response and not anerror
.Same for
PutFederationUpstream
. Both methods use theexecuteResponse
function, which does not parse the response into anErrorResponse{}
error.This is probably intentional, but I've added tests for these cases in case this logic is ever changed.
Additional federation arguments
The federation reference docs show an additional federated queue argument called
consumer-tag
.The
Admin > Federation Upstreams > Add a new upstream
UI shows an additional federated exchange argument calledha-policy
.The federation plugin source shows some additional arguments that may not be documented yet:
resource-cleanup-mode
andbind-nowait
.Perhaps the API should support these additional arguments as well?